在 WebView2 应用中处理本地内容 您所在的位置:网站首页 webview加载本地html cocos creator 在 WebView2 应用中处理本地内容

在 WebView2 应用中处理本地内容

2023-11-06 00:49| 来源: 网络整理| 查看: 265

在 WebView2 应用中处理本地内容 项目 10/25/2023

除了加载远程内容外,还可以将内容本地加载到 WebView2 中。 有几种方法可用于将本地内容加载到 WebView2 控件中,包括:

导航到文件 URL。 导航到 HTML 字符串。 虚拟主机名映射。 WebResourceRequested处理事件。

下面介绍了这些方法。

选择方法

将本地内容加载到 WebView2 控件的各种方法支持以下方案:

应用场景 导航到文件 URL 导航到 HTML 字符串 通过使用虚拟主机名映射 通过使用 WebResourceRequested 基于源的 DOM API ✔️ ❌ ✔️ ✔️ 需要安全上下文的 DOM API ❌ ❌ ✔️ ✔️ 动态内容 ❌ ✔️ ❌ ✔️ 其他 Web 资源 ✔️ ❌ ✔️ ✔️ 在 WebView2 进程中解析的其他 Web 资源 ✔️ ❌ ✔️ ❌

下面将更详细地介绍这些方案。

通过导航到文件 URL 加载本地内容

WebView2 允许导航至文件 URL,以加载基本 HTML 或 PDF。 这是加载本地内容的最简单、最有效的方法。 但是,它不如其他方法灵活。 与在 Web 浏览器中一样,文件 URL 在某些功能上受到限制:

文档的源对其文件路径是唯一的。 这意味着需要源(如 或indexedDB)localStorage的 Web API 将正常工作,但存储的数据将不适用于从其他文件路径加载的其他本地文档。 某些 Web API 仅限于安全 HTTPS URL,不适用于文件 URL 加载的文档。 这包括用于获取视频或声音、navigator.geolocation.getCurrentPosition()访问设备位置或Notification.requestPermission()请求用户显示通知权限等 navigator.mediaDevices.getUserMedia() API。 对于每个资源,必须指定完整路径。 若要允许从文件 URI 引用其他本地文件,或显示应用了 XSL 转换的 XML 文件,可以设置 --allow-file-access-from-files 浏览器参数。 请参阅 CoreWebView2EnvironmentOptions.AdditionalBrowserArguments 属性。 通过导航到文件 URL 加载本地内容的注意事项

文件 URL 的行为与在浏览器中的行为类似。 例如,不能 XMLHttpRequest 在文件 URL 中创建 (XHR) ,因为你不在网页的上下文中工作。

必须为每个资源指定文件的完整路径。 例如:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html 跨源资源

指定文件 URL 时,应用将导航到磁盘上的文件,而不是网络上的域。 因此,无法在生成的文档中使用跨源资源。

基于源的 DOM API

通过文件 URL 加载的文档的源对其文件路径是唯一的,就像在浏览器中一样。 需要源(如 或indexedDB)localStorage的 Web API 将正常工作。 但是,从不同文件 URL 加载的不同文档不被视为来自同一源,并且无法访问相同的存储数据。

需要安全上下文的 DOM API

某些 Web API 仅限于安全 HTTPS URL,不适用于文件 URL 加载的文档。 这包括用于获取视频或声音、navigator.geolocation.getCurrentPosition()访问设备位置或Notification.requestPermission()请求用户显示通知权限等 navigator.mediaDevices.getUserMedia() API。 有关详细信息 ,请参阅保护 MDN 上的上下文。

动态内容

通过文件 URL 加载文档时,文档的内容来自磁盘上的静态文件。 这意味着无法动态修改此本地内容。 这不同于从 Web 服务器加载文档,因为 Web 服务器可以动态生成每个响应。

其他 Web 资源

相对 URL 解析也适用于通过文件 URL 加载的文档。 这意味着加载的文档可以引用其他 Web 资源,例如 CSS、脚本或图像文件,这些文件也通过文件 URL 提供。

在 WebView2 进程中解析的其他 Web 资源

文件 URL 在 WebView2 进程中解析。 此选项比 WebResourceRequested在主机应用进程 UI 线程中解析的速度更快。

用于通过导航到文件 URL 加载本地内容的 API .NET/C# WinRT/C# Win32/C++ CoreWebView2.Navigate 方法 CoreWebView2.Navigate 方法 ICoreWebView2::Navigate 方法 文件 URL 示例

本部分以独立于平台的方式显示本地内容文件路径的文件 URL 的外观。

WebView2 应用需要使用前缀和正斜杠对本地文件 URL 进行 file:/// 编码。 例如,对于演示待办示例,路径为:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html

复制本地文件具有“file”前缀的完整路径:

(可选)克隆 Demos 存储库,以便拥有本地副本。 请参阅安装用于Visual Studio Code的 DevTools 扩展中的步骤 5:克隆演示存储库。

在 Microsoft Edge 中,按 Ctrl+O 打开文件。 打开本地 .html 文件,例如本地克隆的文件 Demos/demo-to-do/index.html:

C:\Users\username\Documents\GitHub\Demos\demo-to-do\index.html

地址栏最初不显示 file:/// 前缀,但以驱动器号开头:

C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html

单击“地址栏”,然后按 “开始 ”键,或按 Ctrl+A 选择整个路径。

整个文件路径(包括) file:/// 将复制到剪贴板缓冲区中,因此可以粘贴包含 file:/// 前缀的完整路径:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html

另请参阅:

演示操作 - 呈现的页面 演示操作 - 源代码 步骤 5:在安装适用于Visual Studio Code的 DevTools 扩展中克隆演示存储库。 导航到文件 URL 的示例 .NET/C# WinRT/C# Win32/C++ webView.CoreWebView2.Navigate( "file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html"); webView.CoreWebView2.Navigate( "file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html"); webView->Navigate( L"file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html"); 通过导航到 HTML 字符串加载本地内容

加载本地内容的另一种方法是 方法 NavigateToString 。 此方法直接从字符串将内容加载到 WebView2 中。 如果要通过应用代码打包内容,或者想要动态创建内容,则这非常有用。

若要加载无法通过 URL 访问的内容,则导航到字符串可能很有用。 例如,如果有 HTML 文档的内存中表示形式,则可以使用 NavigateToString 方法将内容加载到 WebView2 控件中。 如果要避免在将内容加载到控件之前需要将内容写入文件或服务器,这将非常有用。

通过导航到 HTML 字符串加载本地内容的注意事项 基于源的 DOM API

使用 NavigateToString 方法加载的文档的位置设置为 about:blank ,其原点设置为 null。 这意味着不能使用依赖于所定义的源的 Web API,例如 localStorage 或 indexedDB。

需要安全上下文的 DOM API

某些 Web API 仅限于安全的 HTTPS URL,并且对于通过 方法加载的文档不可用, NavigateToString 因为它们的位置设置为 about:blank。 这包括用于获取视频或声音、navigator.geolocation.getCurrentPosition()访问设备位置或Notification.requestPermission()请求用户显示通知权限等 navigator.mediaDevices.getUserMedia() API。 有关详细信息 ,请参阅保护 MDN 上的上下文。

动态内容

通过 NavigateToString 方法加载本地内容时,直接将内容作为参数提供给方法。 这意味着可以在运行时控制内容,并且可以根据需要动态生成内容。

其他 Web 资源

使用 NavigateToString 方法加载本地内容不会使生成的文档引用其他 Web 资源(如 CSS、图像或脚本文件)。 方法仅允许指定 HTML 文档的字符串内容。 若要从 HTML 文档中引用其他 Web 资源,请使用本文中所述的其他方法之一,或在 HTML 文档中内联表示这些附加的 Web 资源。

在 WebView2 进程中解析的其他 Web 资源

NavigateToString 不支持其他 Web 资源,如上所述。

用于通过导航到 HTML 字符串加载本地内容的 API .NET/C# WinRT/C# Win32/C++ CoreWebView2.NavigateToString 方法 CoreWebView2.NavigateToString 方法 ICoreWebView2::NavigateToString 方法 网页的示例字符串表示形式

下面是 演示要执行的操作 网页的字符串表示形式。 为了提高可读性,下面的列表添加了换行。 实际上,这些行串联成一条长线:

`\n \n \n TODO app\n \n \n \n \n \n \n\n \n 📋 My tasks\n \n \n ➕ Add a task\n \n \n \n No tasks defined\n \n\n \x3Cscript src="to-do.js">\x3C/script>\n \n\n `

若要获取上述字符串,请执行以下命令:

转到 演示操作。

右键单击网页,然后选择“ 检查 ”以打开 DevTools。

在 DevTools 的 控制台 中,输入: document.body.parentElement.outerHTML。 控制台输出网页的字符串表示形式:

导航到 HTML 字符串的示例 .NET/C# WinRT/C# Win32/C++ // Define htmlString with the string representation of HTML as above. webView.CoreWebView2.NavigateToString(htmlString); // Define htmlString with the string representation of HTML as above. webView.CoreWebView2.NavigateToString(htmlString); // Define htmlString with the string representation of HTML as above. webView->NavigateToString(htmlString); 使用虚拟主机名映射加载本地内容

在 WebView2 控件中加载本地内容的另一种方法是使用虚拟主机名映射。 这涉及到将本地域名映射到本地文件夹,以便在 WebView2 控件尝试加载该域的资源时,它将改为从指定的本地文件夹位置加载内容。 文档的来源也将是虚拟主机名。

此方法允许使用 CoreWebView2HostResourceAccessKind 枚举指定跨域访问。

由于当前限制,使用虚拟主机名访问的媒体文件加载速度可能会很慢。

使用虚拟主机名映射加载本地内容的注意事项 基于源的 DOM API

通过虚拟主机名映射加载的本地内容会导致文档中具有 HTTP 或 HTTPS URL 以及相应的源。 这意味着需要源(如 或indexedDB)localStorage的 Web API 将正常工作,属于同一源的其他文档将能够使用存储的数据。 有关详细信息,请参阅 MDN 上的 同源策略 。

需要安全上下文的 DOM API

某些 Web API 仅限于安全 HTTPS URL。 使用虚拟主机名映射可为本地内容提供 HTTPS URL。 这意味着可以使用 API,例如 navigator.mediaDevices.getUserMedia() 获取视频或声音、 navigator.geolocation.getCurrentPosition() 访问设备位置或 Notification.requestPermission() 请求用户显示通知的权限。 有关详细信息 ,请参阅保护 MDN 上的上下文。

动态内容

通过虚拟主机名映射加载本地内容时,你将将虚拟主机名映射到包含磁盘上静态文件的本地文件夹。 这意味着无法动态修改此本地内容。 这不同于从 Web 服务器加载文档,因为 Web 服务器可以动态生成每个响应。

其他 Web 资源

通过虚拟主机名映射加载的本地内容具有支持相对 URL 解析的 HTTP 或 HTTPS URL。 这意味着加载的文档可以引用其他 Web 资源,例如 CSS、脚本或图像文件,这些资源也通过虚拟主机名映射提供。

在 WebView2 进程中解析的其他 Web 资源

虚拟主机名 URL 在 WebView2 进程中解析。 此选项比 WebResourceRequested在主机应用进程 UI 线程中解析的速度更快。

使用虚拟主机名映射加载本地内容的 API .NET/C# WinRT/C# Win32/C++ CoreWebView2.SetVirtualHostNameToFolderMapping 方法 CoreWebView2.ClearVirtualHostNameToFolderMapping 方法 CoreWebView2HostResourceAccessKind Enum CoreWebView2.SetVirtualHostNameToFolderMapping 方法 CoreWebView2.ClearVirtualHostNameToFolderMapping 方法 CoreWebView2HostResourceAccessKind Enum ICoreWebView2_3::SetVirtualHostNameToFolderMapping 方法 ICoreWebView2_3::ClearVirtualHostNameToFolderMapping 方法 COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND枚举 虚拟主机名映射示例 .NET/C# WinRT/C# Win32/C++ webView.CoreWebView2.SetVirtualHostNameToFolderMapping("demo", "C:\Github\Demos\demo-to-do", CoreWebView2HostResourceAccessKind.DenyCors); webView.CoreWebView2.Navigate("https://demo/index.html"); Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder; Windows.Storage.StorageFolder demo = await storageFolder.GetFolderAsync("Demo"); webView.CoreWebView2.SetVirtualHostNameToFolderMapping("demo", demo.Path, CoreWebView2HostResourceAccessKind.DenyCors); webView.CoreWebView2.Navigate("https://demo/index.html"); webView->SetVirtualHostNameToFolderMapping(L"demo", L"C:\\Github\\Demos\\demo-to-do", COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS); webView->Navigate(L"https://demo/index.html"); 通过处理 WebResourceRequested 事件加载本地内容

在 WebView2 控件中托管本地内容的另一种方法是依赖于 WebResourceRequested 事件。 当控件尝试加载资源时,会触发此事件。 可以使用此事件来截获请求并提供本地内容,如 网络请求的自定义管理中所述。

WebResourceRequested 允许你基于每个请求自定义本地内容的行为。 这意味着你可以决定要拦截哪些请求并提供你自己的内容,以及让 WebView2 控件正常处理的请求。 但是,自定义行为需要更多代码(例如虚拟主机映射),并且需要了解 HTTP 才能构造正确的响应。

从 WebView2 的角度来看,资源将通过网络提供,WebView2 将遵循应用在响应过程中设置的标头。 WebResourceRequested使用事件的速度也比其他方法慢,因为每个请求都需要跨进程通信和处理。

通过处理 WebResourceRequested 事件加载本地内容的注意事项 基于源的 DOM API

通过 WebResourceRequested 加载的本地内容会导致文档中具有 HTTP 或 HTTPS URL 以及相应的源。 这意味着需要源(如 或indexedDB)localStorage的 Web API 将正常工作,属于同一源的其他文档将能够使用存储的数据。 有关详细信息,请参阅 MDN 上的 同源策略 。

需要安全上下文的 DOM API

某些 Web API 仅限于安全 HTTPS URL。 使用 WebResourceRequested 可将 HTTPS URL Web 资源请求替换为自己的本地内容。 这意味着可以使用 API,例如 navigator.mediaDevices.getUserMedia() 获取视频或声音、 navigator.geolocation.getCurrentPosition() 访问设备位置或 Notification.requestPermission() 请求用户显示通知的权限。 有关详细信息 ,请参阅保护 MDN 上的上下文。

动态内容

通过 WebResourceRequested加载本地内容时,指定要在事件处理程序中加载的本地内容。 这意味着可以在运行时控制内容,并且可以根据需要动态生成内容。

其他 Web 资源

WebResourceRequested 修改通过 HTTP 或 HTTPS URL 加载的内容,这些 URL 支持相对 URL 解析。 这意味着生成的文档可以引用其他 Web 资源,例如 CSS、脚本或图像文件,这些文件也通过 WebResourceRequested提供。

在 WebView2 进程中解析的其他 Web 资源

通过文件 URL 或虚拟主机名映射加载内容时,解析将在 WebView2 进程中发生。 但是,该 WebResourceRequested 事件在主机应用进程的 WebView2 UI 线程上引发,这可能会导致生成的文档加载速度变慢。

WebView2 首先暂停加载网页,以便等待事件发送到主机应用进程。 然后,WebView2 等待 UI 线程可用。 然后,WebView2 等待应用代码处理事件。

这可能需要一些时间。 请确保仅对必须引发WebResourceRequested事件的 Web 资源的调用AddWebResourceRequestedFilter。

用于通过处理 WebResourceRequested 事件加载本地内容的 API .NET/C# WinRT/C# Win32/C++ CoreWebView2.NavigateWithWebResourceRequest 方法 CoreWebView2.WebResourceRequested 事件 CoreWebView2.NavigateWithWebResourceRequest 方法 CoreWebView2.WebResourceRequested 事件 ICoreWebView2_2::NavigateWithWebResourceRequest 方法 ICoreWebView2::WebResourceRequested 事件 (添加、 删除) 处理 WebResourceRequested 事件的示例 .NET/C# WinRT/C# Win32/C++ // Reading of response content stream happens asynchronously, and WebView2 does not // directly dispose the stream once it read. Therefore, use the following stream // class, which properly disposes when WebView2 has read all data. For details, see // [CoreWebView2 does not close stream content](https://github.com/MicrosoftEdge/WebView2Feedback/issues/2513). class ManagedStream : Stream { public ManagedStream(Stream s) { s_ = s; } public override bool CanRead => s_.CanRead; public override bool CanSeek => s_.CanSeek; public override bool CanWrite => s_.CanWrite; public override long Length => s_.Length; public override long Position { get => s_.Position; set => s_.Position = value; } public override void Flush() { throw new NotImplementedException(); } public override long Seek(long offset, SeekOrigin origin) { return s_.Seek(offset, origin); } public override void SetLength(long value) { throw new NotImplementedException(); } public override int Read(byte[] buffer, int offset, int count) { int read = 0; try { read = s_.Read(buffer, offset, count); if (read == 0) { s_.Dispose(); } } catch { s_.Dispose(); throw; } return read; } public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); } private Stream s_; } webView.CoreWebView2.AddWebResourceRequestedFilter("https://demo/*", CoreWebView2WebResourceContext.All); webView.CoreWebView2.WebResourceRequested += delegate (object sender, CoreWebView2WebResourceRequestedEventArgs args) { string assetsFilePath = "C:\\Demo\\" + args.Request.Uri.Substring("https://demo/*".Length - 1); try { FileStream fs = File.OpenRead(assetsFilePath); ManagedStream ms = new ManagedStream(fs); string headers = ""; if (assetsFilePath.EndsWith(".html")) { headers = "Content-Type: text/html"; } else if (assetsFilePath.EndsWith(".jpg")) { headers = "Content-Type: image/jpeg"; } else if (assetsFilePath.EndsWith(".png")) { headers = "Content-Type: image/png"; } else if (assetsFilePath.EndsWith(".css")) { headers = "Content-Type: text/css"; } else if (assetsFilePath.EndsWith(".js")) { headers = "Content-Type: application/javascript"; } args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse( ms, 200, "OK", headers); } catch (Exception) { args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse( null, 404, "Not found", ""); } }; class ManagedStream : IRandomAccessStream { public ManagedStream(IRandomAccessStream s) { s_ = s; } public override bool CanRead => s_.CanRead; public override bool CanSeek => s_.CanSeek; public override bool CanWrite => s_.CanWrite; ulong IRandomAccessStream.Position => { get => s_.Position; set => s_.Position = value; } public ulong Size => s_.Size; public IInputStream GetInputStreamAt(ulong position) { throw new NotImplementedException(); } public IOutputStream GetOutputStreamAt(ulong position) { throw new NotImplementedException(); } public void Seek(ulong position) { return s_.Seek(position); } public IRandomAccessStream CloneStream() { throw new NotImplementedException(); } public IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) { IAsyncOperationWithProgress result; try { result = s_.ReadAsync(buffer, count, options); // Once read is complete if no data was read, dispose the underlying // stream. result.Completed += new AsyncOperationWithProgressCompletedHandler( delegate (IAsyncOperationWithProgress asyncInfo, AsyncStatus asyncStatus) { if (asyncInfo.GetResults().Length == 0) { s_.Dispose(); } }); } catch { s_.Dispose(); throw; } return result; } public IAsyncOperationWithProgress WriteAsync(IBuffer buffer) { throw new NotImplementedException(); } public IAsyncOperation FlushAsync() { throw new NotImplementedException(); } public void Dispose() { throw new NotImplementedException(); } private IRandomAccessStream s_; } WebView2.CoreWebView2.AddWebResourceRequestedFilter("https://demo/*", CoreWebView2WebResourceContext.All); WebView2.CoreWebView2.WebResourceRequested += async delegate (CoreWebView2 sender, CoreWebView2WebResourceRequestedEventArgs args) { string filename = args.Request.Uri.Substring("https://demo/*".Length - 1); try { Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder; Windows.Storage.StorageFolder demo = await storageFolder.GetFolderAsync("Demo"); Windows.Storage.StorageFile asset = await demo.GetFileAsync(filename); ManagedStream ms = new ManagedStream(await asset.OpenReadAsync()); string headers = ""; if (filename.EndsWith(".html")) { headers = "Content-Type: text/html"; } else if (filename.EndsWith(".jpg")) { headers = "Content-Type: image/jpeg"; } else if (filename.EndsWith(".png")) { headers = "Content-Type: image/png"; } else if (filename.EndsWith(".css")) { headers = "Content-Type: text/css"; } else if (filename.EndsWith(".js")) { headers = "Content-Type: application/javascript"; } args.Response = WebView2.CoreWebView2.Environment.CreateWebResourceResponse(ms, 200, "OK", headers); } catch (Exception) { args.Response = WebView2.CoreWebView2.Environment.CreateWebResourceResponse(null, 404, "Not found", ""); } }; CHECK_FAILURE(m_webView->AddWebResourceRequestedFilter( L"https://demo/*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)); CHECK_FAILURE(m_webView->add_WebResourceRequested( Callback( this, &AppWindow::WebResourceRequestedEventHandler) .Get(), &m_webResourceRequestedToken)); HRESULT AppWindow::WebResourceRequestedEventHandler( ICoreWebView2* webview, ICoreWebView2WebResourceRequestedEventArgs* args) { wil::com_ptr request; wil::com_ptr response; wil::com_ptr stream; CHECK_FAILURE(args->get_Request(&request)); wil::unique_cotaskmem_string uri; CHECK_FAILURE(request->get_Uri(&uri)); std::wstring assetsFilePath = L"C:\\Demo"; assetsFilePath += (uri.get() + ARRAYSIZE(L"https://demo")); wil::com_ptr stream; SHCreateStreamOnFileEx( assetsFilePath.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream); if (stream) { std::wstring headers; if (assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) == L"html") { headers = L"Content-Type: text/html"; } else if ( assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) == L"jpg") { headers = L"Content-Type: image/jpeg"; } else if ( assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) == L"png") { headers = L"Content-Type: image/png"; } else if ( assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) == L"css") { headers = L"Content-Type: text/css"; } else if ( assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) == L"js") { headers = L"Content-Type: application/javascript"; } CHECK_FAILURE( webViewEnvironment->CreateWebResourceResponse( stream.get(), 200, L"OK", headers.c_str(), &response)); CHECK_FAILURE(args->put_Response(response.get())); } else { CHECK_FAILURE( webViewEnvironment->CreateWebResourceResponse( nullptr, 404, L"Not Found", L"", &response)); CHECK_FAILURE(args->put_Response(response.get())); } } 另请参阅 在 WebView2 功能和 API 概述中管理加载到 WebView2 中的内容 演示呈现的“操作”页 演示要完成的源代码


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有